#include "core/editor/code_editor.h"
#include <QPainter>
#include <QTextBlock>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QScrollBar>

CodeEditor::CodeEditor(QWidget *parent) 
    : QPlainTextEdit(parent), fileName(""), highlighter(nullptr) {
    lineNumberArea = new LineNumberArea(this);
    
    connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
    connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea);
    connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
    
    updateLineNumberAreaWidth(0);
    highlightCurrentLine();
    
    setupEditor();
}


ompleter = new QCompleter(this);
completer->setWidget(this);
completer->setCompletionMode(QCompleter::PopupCompletion);
completer->setCaseSensitivity(Qt::CaseInsensitive);

codeCompleter = new CodeCompleter(this);

connect(completer, QOverload<const QString &>::of(&QCompleter::activated),
        this, &CodeEditor::insertCompletion);

QString CodeEditor::textUnderCursor() const {
    QTextCursor tc = textCursor();
    tc.select(QTextCursor::WordUnderCursor);
    return tc.selectedText();
}

CodeEditor::~CodeEditor() {
    delete highlighter;
}

void CodeEditor::keyPressEvent(QKeyEvent *e) {
    if (completer && completer->popup()->isVisible()) {
        switch (e->key()) {
        case Qt::Key_Enter:
        case Qt::Key_Return:
        case Qt::Key_Escape:
        case Qt::Key_Tab:
        case Qt::Key_Backtab:
            e->ignore();
            return;
        default:
            break;
        }
    }
    
    bool isShortcut = (e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space;
    if (!isShortcut)
        QPlainTextEdit::keyPressEvent(e);
    
    const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
    if (ctrlOrShift && e->text().isEmpty())
        return;
    
    static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=");
    bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
    QString completionPrefix = textUnderCursor();
    
    if (!isShortcut && (hasModifier || e->text().isEmpty() 
                       || completionPrefix.length() < 2 
                       || eow.contains(e->text().right(1)))) {
        completer->popup()->hide();
        return;
    }
    
    if (completionPrefix != completer->completionPrefix()) {
        // 更新补全模型
        codeCompleter->updateCompletionModel(toPlainText());
        QStringList completions = codeCompleter->getCompletions(completionPrefix);
        
        QAbstractItemModel *model = new QStringListModel(completions, completer);
        completer->setModel(model);
        completer->setCompletionPrefix(completionPrefix);
    }
    
    QRect cr = cursorRect();
    cr.setWidth(completer->popup()->sizeHintForColumn(0)
      + completer->popup()->verticalScrollBar()->sizeHint().width());
    completer->complete(cr);
}

void CodeEditor::insertCompletion(const QString &completion) {
    if (completer->widget() != this)
        return;
    
    QTextCursor tc = textCursor();
    int extra = completion.length() - completer->completionPrefix().length();
    tc.movePosition(QTextCursor::Left);
    tc.movePosition(QTextCursor::EndOfWord);
    tc.insertText(completion.right(extra));
    setTextCursor(tc);
}
void CodeEditor::setupEditor() {
    QFont font;
    font.setFamily("Consolas");
    font.setFixedPitch(true);
    font.setPointSize(12);
    setFont(font);
    
    // 设置制表符宽度
    setTabStopDistance(fontMetrics().horizontalAdvance(' ') * 4);
}

void CodeEditor::loadFile(const QString &fileName) {
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, "警告", QString("无法打开文件 %1:\n%2.")
                             .arg(QDir::toNativeSeparators(fileName), file.errorString()));
        return;
    }
    
    QTextStream in(&file);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    setPlainText(in.readAll());
    QApplication::restoreOverrideCursor();
    
    this->fileName = fileName;
    emit fileNameChanged(fileName);
}

bool CodeEditor::save() {
    if (fileName.isEmpty()) {
        return saveAs();
    }
    
    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, "警告", QString("无法写入文件 %1:\n%2.")
                             .arg(QDir::toNativeSeparators(fileName), file.errorString()));
        return false;
    }
    
    QTextStream out(&file);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    out << toPlainText();
    QApplication::restoreOverrideCursor();
    
    return true;
}

bool CodeEditor::saveAs() {
    QString newFileName = QFileDialog::getSaveFileName(this, "另存为");
    if (newFileName.isEmpty()) {
        return false;
    }
    
    fileName = newFileName;
    emit fileNameChanged(fileName);
    return save();
}

void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) {
    QPainter painter(lineNumberArea);
    painter.fillRect(event->rect(), Qt::lightGray);
    
    QTextBlock block = firstVisibleBlock();
    int blockNumber = block.blockNumber();
    int top = (int)blockBoundingGeometry(block).translated(contentOffset()).top();
    int bottom = top + (int)blockBoundingRect(block).height();
    
    while (block.isValid() && top <= event->rect().bottom()) {
        if (block.isVisible() && bottom >= event->rect().top()) {
            QString number = QString::number(blockNumber + 1);
            painter.setPen(Qt::black);
            painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
                             Qt::AlignRight, number);
        }
        
        block = block.next();
        top = bottom;
        bottom = top + (int)blockBoundingRect(block).height();
        ++blockNumber;
    }
}

int CodeEditor::lineNumberAreaWidth() {
    int digits = 1;
    int max = qMax(1, blockCount());
    while (max >= 10) {
        max /= 10;
        ++digits;
    }
    
    int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
    return space;
}

void CodeEditor::resizeEvent(QResizeEvent *event) {
    QPlainTextEdit::resizeEvent(event);
    
    QRect cr = contentsRect();
    lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}

void CodeEditor::updateLineNumberAreaWidth(int newBlockCount) {
    Q_UNUSED(newBlockCount);
    setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}

void CodeEditor::highlightCurrentLine() {
    QList<QTextEdit::ExtraSelection> extraSelections;
    
    if (!isReadOnly()) {
        QTextEdit::ExtraSelection selection;
        
        QColor lineColor = QColor(Qt::yellow).lighter(160);
        
        selection.format.setBackground(lineColor);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = textCursor();
        selection.cursor.clearSelection();
        extraSelections.append(selection);
    }
    
    setExtraSelections(extraSelections);
}

void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) {
    if (dy)
        lineNumberArea->scroll(0, dy);
    else
        lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
    
    if (rect.contains(viewport()->rect()))
        updateLineNumberAreaWidth(0);
}

// 在CodeEditor类中添加方法
void CodeEditor::findNext(const QString &text, bool caseSensitive, bool wholeWord) {
    QTextDocument::FindFlags flags;
    if (caseSensitive) flags |= QTextDocument::FindCaseSensitively;
    if (wholeWord) flags |= QTextDocument::FindWholeWords;
    
    bool found = find(text, flags);
    if (!found) {
        // 从文档开始处重新查找
        QTextCursor cursor(document());
        setTextCursor(cursor);
        find(text, flags);
    }
}

void CodeEditor::replace(const QString &findText, const QString &replaceText, 
                        bool caseSensitive, bool wholeWord) {
    QTextCursor cursor = textCursor();
    if (cursor.hasSelection() && 
        cursor.selectedText().compare(findText, 
            caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) == 0) {
        cursor.insertText(replaceText);
    }
    findNext(findText, caseSensitive, wholeWord);
}

void CodeEditor::replaceAll(const QString &findText, const QString &replaceText, 
                           bool caseSensitive, bool wholeWord) {
    QTextCursor cursor(document());
    setTextCursor(cursor);
    
    QTextDocument::FindFlags flags;
    if (caseSensitive) flags |= QTextDocument::FindCaseSensitively;
    if (wholeWord) flags |= QTextDocument::FindWholeWords;
    
    int count = 0;
    while (find(findText, flags)) {
        textCursor().insertText(replaceText);
        count++;
    }
    
    QMessageBox::information(this, "替换完成", 
                           QString("共替换 %1 处").arg(count));
}
